﻿#include "WindowImplBase.h"

#include <QPainter>
#include <QtMath>
#include <QVariant>
#include <QEvent>
#include <QKeyEvent>
#include <QDebug>
#include <QApplication>
#include <QDesktopWidget>

namespace GeneralUI {

	/** 鼠标移动判断，偏移超过2px才算鼠标移动
	 */
	const int MIN_MOVE_OFFSET = 2;

	/** 宏定义字符串属性
	 */
#define IS_CAPTION_PROPERTY             "isCaption"
#define LAST_MOUSE_TRACK_PROPERTY       "lastMouseTrackProperty"

	WindowImplBase::WindowImplBase(QWidget *parent)
		: QDialog(parent)
		, m_bEnableMinMax(false)
		, m_shadowMargin(5, 4, 5, 6)
		, m_nJudgeResizeDistance(8)
		, m_eShowState(SHOW_NORMAL)
		, m_eMinimizedRecoverState(SHOW_NORMAL)
		, m_eOpType(OP_NONE)
		, m_eDirection(NONE)
	{
		setWindowFlags(windowFlags() | Qt::FramelessWindowHint | Qt::WindowMinMaxButtonsHint);
		setAttribute(Qt::WA_TranslucentBackground);
		setFocusPolicy(Qt::FocusPolicy::StrongFocus);
		setFocus();
		installEventFilter(this);
	}

	WindowImplBase::~WindowImplBase()
	{
	}

	void WindowImplBase::setEnableWindowMinMax(bool bEnable)
	{
		// 固定大小的窗口不支持改变大小
		if (this->maximumWidth() == this->minimumWidth() ||
			this->maximumHeight() == this->minimumHeight())
		{
			return;
		}

		if (bEnable != m_bEnableMinMax)
		{
			m_bEnableMinMax = bEnable;
			QList<QObject*> subObjList = this->findChildren<QObject*>();

			if (m_bEnableMinMax)
			{
				this->setProperty(LAST_MOUSE_TRACK_PROPERTY, this->hasMouseTracking());
				this->setMouseTracking(m_bEnableMinMax);

				for (auto obj : subObjList)
				{
					if (obj->isWidgetType())
					{
						QWidget* pWidget = dynamic_cast<QWidget*>(obj);
						// 过滤QSlider
						if (pWidget && !pWidget->inherits("QSlider"))
						{
							pWidget->setProperty(LAST_MOUSE_TRACK_PROPERTY, pWidget->hasMouseTracking());
							pWidget->setMouseTracking(m_bEnableMinMax);
						}
					}
				}
			}
			else
			{
				// 还原之前的配置
				if (this->property(LAST_MOUSE_TRACK_PROPERTY).isValid())
				{
					this->setMouseTracking(this->property(LAST_MOUSE_TRACK_PROPERTY).toBool());
				}
				for (auto obj : subObjList)
				{
					if (obj->isWidgetType())
					{
						QWidget* pWidget = dynamic_cast<QWidget*>(obj);
						if (pWidget && pWidget->property(LAST_MOUSE_TRACK_PROPERTY).isValid())
						{
							pWidget->setMouseTracking(pWidget->property(LAST_MOUSE_TRACK_PROPERTY).toBool());
						}
					}
				}
			}
		}
	}

	void WindowImplBase::setWindowShadowMargins(const QMargins& shadowMargin)
	{
		if (shadowMargin != m_shadowMargin)
		{
			m_shadowMargin = shadowMargin;
		}
	}

	void WindowImplBase::setJudgeResizeDistacne(const int nDistacne)
	{
		if (m_nJudgeResizeDistance != nDistacne)
		{
			m_nJudgeResizeDistance = nDistacne;
		}
	}

	void WindowImplBase::showNormal()
	{
		if (m_eShowState != SHOW_NORMAL)
		{
			adjustRect(m_normalShowRect);
			m_eShowState = SHOW_NORMAL;
			// 发送状态变化信号
			emit signalWindowStateChanged(m_eShowState);
		}
	}

	void WindowImplBase::showMaximized()
	{
		if (m_eShowState != SHOW_MAXIMIZED)
		{
			// 保存正常显示的窗口大小，恢复显示需要
			if (m_eShowState == SHOW_NORMAL)
			{
				QPoint topLeft = this->mapToGlobal(this->rect().topLeft());
				QPoint bottomRight = this->mapToGlobal(this->rect().bottomRight());
				m_normalShowRect = QRect(topLeft, bottomRight);
			}

			// 在窗口最大化时，为了不显示阴影边框，将窗口大小向外扩充阴影边框大小
			int nXMargin = m_shadowMargin.left() + m_shadowMargin.right();
			int nYMargin = m_shadowMargin.top() + m_shadowMargin.bottom();

			// 最大化到鼠标所在的屏幕
			int nScreenCount = QApplication::desktop()->screenCount();
			QRect screenRect = QApplication::desktop()->screenGeometry();
			QPoint mousePos = QCursor::pos();

			for (int i = 0; i < nScreenCount; i++)
			{
				QRect rect = QApplication::desktop()->screenGeometry(i);
				if (rect.contains(mousePos))
				{
					screenRect = rect;
					break;
				}
			}
			QRect destRect(-m_shadowMargin.left() + screenRect.left(), -m_shadowMargin.top() + screenRect.top(), screenRect.width() + nXMargin, screenRect.height() + nYMargin);

			// 调整窗口大小
			adjustRect(destRect);
			m_eShowState = SHOW_MAXIMIZED;
			// 发送状态变化信号
			emit signalWindowStateChanged(m_eShowState);
		}
	}

	void WindowImplBase::showMinimized()
	{
		// 只有顶层窗口才允许最小化
		if (m_eShowState != SHOW_MINIMIZED && !parent())
		{
			// 保存正常显示的窗口大小，恢复显示需要
			if (m_eShowState == SHOW_NORMAL)
			{
				QPoint topLeft = this->mapToGlobal(this->rect().topLeft());
				QPoint bottomRight = this->mapToGlobal(this->rect().bottomRight());
				m_normalShowRect = QRect(topLeft, bottomRight);
			}

			// 保存最小化之前的状态，最小化还原时需要
			m_eMinimizedRecoverState = m_eShowState;
			m_eShowState = SHOW_MINIMIZED;
			__super::showMinimized();
			// 发送状态变化信号
			emit signalWindowStateChanged(m_eShowState);
		}
	}

	bool WindowImplBase::isMaximized() const
	{
		return m_eShowState == SHOW_MAXIMIZED;
	}

	bool WindowImplBase::isMinimized() const
	{
		return m_eShowState == SHOW_MINIMIZED;
	}

	void WindowImplBase::paintEvent(QPaintEvent * event)
	{
		QPainterPath path;
		path.setFillRule(Qt::WindingFill);
		path.addRect(10, 10, this->width() - 20, this->height() - 20);

		QPainter painter(this);
		painter.setRenderHint(QPainter::Antialiasing, true);
		painter.fillPath(path, QBrush(Qt::white));

		QColor color(0, 0, 0, 50);
		for (int i = 0; i < 10; i++)
		{
			QPainterPath path;
			path.setFillRule(Qt::WindingFill);
			path.addRect(10 - i, 10 - i, this->width() - (10 - i) * 2, this->height() - (10 - i) * 2);
			color.setAlpha(150 - qSqrt(i) * 50);
			painter.setPen(color);
			painter.drawPath(path);
		}
	}

	bool WindowImplBase::eventFilter(QObject *obj, QEvent *event)
	{
		QDialog *pDialog = qobject_cast<QDialog *>(obj);

		if (pDialog == this)
		{
			if (QEvent::KeyPress == event->type())
			{
				QKeyEvent *pKeyEvent = static_cast<QKeyEvent*>(event);
				if (pKeyEvent->key() == Qt::Key_Escape)
				{
					return true;
				}
			}
			else if (QEvent::WindowDeactivate == event->type() || QEvent::FocusOut == event->type())
			{
				// 窗口触发停用状态以及失焦，则强制结束操作
				stopOperate();
			}
			else if (QEvent::WindowStateChange == event->type())
			{
				QWindowStateChangeEvent* pStateEvent = dynamic_cast<QWindowStateChangeEvent*>(event);
				Qt::WindowStates eOldStates = pStateEvent->oldState();
				Qt::WindowStates eNewStates = this->windowState();
				// 窗口最小化状态变化
				bool bShowMixStateChanged = (eOldStates & Qt::WindowMinimized) != (eNewStates & Qt::WindowMinimized);

				if (bShowMixStateChanged && isMinimized())
				{
					// 最小化恢复显示
					if (m_eMinimizedRecoverState == SHOW_MAXIMIZED)
						showMaximized();
					else if (m_eMinimizedRecoverState == SHOW_NORMAL)
						showNormal();
				}
			}
		}

		return QDialog::eventFilter(obj, event);
	}

	void WindowImplBase::setVisible(bool visible)
	{
		if (visible && !isActiveWindow())
		{
			activateWindow();
		}

		return QDialog::setVisible(visible);
	}

	void WindowImplBase::mouseMoveEvent(QMouseEvent *event)
	{
		QPoint mousePos = event->globalPos();

		if (m_eOpType == OP_NONE && m_bEnableMinMax)
		{
			judgeRelationshipPointAndRegions(mousePos);
		}
		else if (m_eOpType == OP_MOVE)
		{
			QPoint offsetPoint = mousePos - m_pressPoint;
			// 判断鼠标是否移动
			if (qAbs(offsetPoint.x()) >= MIN_MOVE_OFFSET || qAbs(offsetPoint.y()) >= MIN_MOVE_OFFSET)
			{
				QPoint movePos = this->pos() + offsetPoint;
				this->move(movePos);
				m_pressPoint = mousePos;

				// 拖拽移动后，取消最大化标志，并恢复大小
				if (m_bEnableMinMax)
					recoverSizeWhenMaximizedMove(mousePos);
			}
		}
		else if (m_eOpType == OP_RESIZE)
		{
			QPoint offsetPoint = mousePos - m_pressPoint;
			// 判断鼠标是否移动
			if (qAbs(offsetPoint.x()) >= MIN_MOVE_OFFSET || qAbs(offsetPoint.y()) >= MIN_MOVE_OFFSET)
			{
				// 调整窗口大小
				resizeWindowByMouseMove(mousePos);
			}
		}
	}

	void WindowImplBase::mousePressEvent(QMouseEvent *event)
	{
		if (event->button() == Qt::LeftButton)
		{
			m_pressPoint = event->globalPos();

			if (m_eDirection == NONE)
			{
				// 判断鼠标是否执行移动操作
				QWidget* pWidget = this->childAt(this->mapFromGlobal(m_pressPoint));
				if (pWidget && pWidget->property(IS_CAPTION_PROPERTY).isValid() && pWidget->property(IS_CAPTION_PROPERTY).toBool())
				{
					m_eOpType = OP_MOVE;
				}
			}
			else
			{
				m_eOpType = OP_RESIZE;
			}
		}
		else
		{
			stopOperate();
		}
	}

	void WindowImplBase::mouseReleaseEvent(QMouseEvent *event)
	{
		if (m_eOpType != OP_NONE)
		{
			stopOperate();
		}
	}

	void WindowImplBase::mouseDoubleClickEvent(QMouseEvent *event)
	{
		// 鼠标双击全屏/恢复
		if (event->button() == Qt::LeftButton && m_bEnableMinMax)
		{
			QWidget* pWidget = this->childAt(this->mapFromGlobal(event->globalPos()));
			if (pWidget && pWidget->property(IS_CAPTION_PROPERTY).isValid() && pWidget->property(IS_CAPTION_PROPERTY).toBool())
			{
				if (m_eShowState != SHOW_MAXIMIZED)
				{
					showMaximized();
				}
				else
				{
					showNormal();
				}
			}
		}
	}

	void WindowImplBase::adjustRect(const QRect& rect)
	{
		this->setGeometry(rect);
	}

	void WindowImplBase::judgeRelationshipPointAndRegions(const QPoint& point)
	{
		if (m_eShowState != SHOW_NORMAL)
		{
			return;
		}

		QRect rect = this->rect();
		QPoint topLeft = this->mapToGlobal(rect.topLeft());
		QPoint bottomRight = this->mapToGlobal(rect.bottomRight());
		QRect globalRect(topLeft, bottomRight);

		// 两点半径
		QCursor cursor = Qt::ArrowCursor;
		m_eDirection = NONE;

		if (distanceFromTwoPoint(point, globalRect.topLeft()) <= m_nJudgeResizeDistance)
		{
			cursor = Qt::SizeFDiagCursor;
			m_eDirection = TOP_LEFT;
		}
		else if (distanceFromTwoPoint(point, globalRect.bottomRight()) <= m_nJudgeResizeDistance)
		{
			cursor = Qt::SizeFDiagCursor;
			m_eDirection = BOTTOM_RIGHT;
		}
		else if (distanceFromTwoPoint(point, globalRect.bottomLeft()) <= m_nJudgeResizeDistance)
		{
			cursor = Qt::SizeBDiagCursor;
			m_eDirection = BOTTOM_LEFT;
		}
		else if (distanceFromTwoPoint(point, globalRect.topRight()) <= m_nJudgeResizeDistance)
		{
			cursor = Qt::SizeBDiagCursor;
			m_eDirection = TOP_RIGHT;
		}
		else if (qAbs(globalRect.left() - point.x()) <= m_nJudgeResizeDistance)
		{
			cursor = Qt::SizeHorCursor;
			m_eDirection = LEFT;
		}
		else if (qAbs(globalRect.right() - point.x()) <= m_nJudgeResizeDistance)
		{
			cursor = Qt::SizeHorCursor;
			m_eDirection = RIGHT;
		}
		else if (qAbs(globalRect.top() - point.y()) <= m_nJudgeResizeDistance)
		{
			cursor = Qt::SizeVerCursor;
			m_eDirection = TOP;
		}
		else if (qAbs(globalRect.bottom() - point.y()) <= m_nJudgeResizeDistance)
		{
			cursor = Qt::SizeVerCursor;
			m_eDirection = BOTTOM;
		}
		this->setCursor(cursor);
	}

	double WindowImplBase::distanceFromTwoPoint(QPointF p1, QPointF p2)
	{
		double dx = qAbs(p1.rx() - p2.rx());
		double dy = qAbs(p1.ry() - p2.ry());
		return qSqrt(dx * dx + dy * dy);
	}

	void WindowImplBase::resizeWindowByMouseMove(const QPoint& mousePos)
	{
		QRect rect = this->rect();
		QPoint topLeft = this->mapToGlobal(rect.topLeft());
		QPoint bottomRight = this->mapToGlobal(rect.bottomRight());
		QRect globalRect(topLeft, bottomRight);

		int nMinWidth = minimumWidth();
		int nMinHeight = minimumHeight();

		switch (m_eDirection)
		{
		case TOP:
		{
			int nTopPos = mousePos.y();
			if (globalRect.bottom() - nTopPos < nMinHeight)
			{
				nTopPos = globalRect.bottom() - nMinHeight;
			}
			globalRect.setTop(nTopPos);
			break;
		}
		case BOTTOM:
		{
			int nButtomPos = mousePos.y();
			if (nButtomPos - globalRect.top() < nMinHeight)
			{
				nButtomPos = globalRect.top() + nMinHeight;
			}
			globalRect.setBottom(nButtomPos);
			break;
		}
		case LEFT:
		{
			int nLeftPos = mousePos.x();
			if (globalRect.right() - nLeftPos < nMinWidth)
			{
				nLeftPos = globalRect.right() - nMinWidth;
			}
			globalRect.setLeft(nLeftPos);
			break;
		}
		case RIGHT:
		{
			int nRightPos = mousePos.x();
			if (nRightPos - globalRect.left() < nMinWidth)
			{
				nRightPos = globalRect.left() + nMinWidth;
			}
			globalRect.setRight(nRightPos);
			break;
		}
		case TOP_LEFT:
		{
			int nLeft = mousePos.x();
			int nTop = mousePos.y();

			if (globalRect.right() - nLeft < nMinWidth)
			{
				nLeft = globalRect.right() - nMinWidth;
			}
			if (globalRect.bottom() - nTop < nMinHeight)
			{
				nTop = globalRect.bottom() - nMinHeight;
			}
			globalRect.setTopLeft(QPoint(nLeft, nTop));
			break;
		}
		case TOP_RIGHT:
		{
			int nRight = mousePos.x();
			int nTop = mousePos.y();

			if (nRight - globalRect.left() < nMinWidth)
			{
				nRight = globalRect.left() + nMinWidth;
			}
			if (globalRect.bottom() - nTop < nMinHeight)
			{
				nTop = globalRect.bottom() - nMinHeight;
			}
			globalRect.setTopRight(QPoint(nRight, nTop));
			break;
		}
		case BOTTOM_LEFT:
		{
			int nLeft = mousePos.x();
			int nBottom = mousePos.y();

			if (globalRect.right() - nLeft < nMinWidth)
			{
				nLeft = globalRect.right() - nMinWidth;
			}
			if (nBottom - globalRect.top() < nMinHeight)
			{
				nBottom = globalRect.top() + nMinHeight;
			}
			globalRect.setBottomLeft(QPoint(nLeft, nBottom));
			break;
		}
		case BOTTOM_RIGHT:
		{
			int nRight = mousePos.x();
			int nBottom = mousePos.y();

			if (nRight - globalRect.left() < nMinWidth)
			{
				nRight = globalRect.left() + nMinWidth;
			}
			if (nBottom - globalRect.top() < nMinHeight)
			{
				nBottom = globalRect.top() + nMinHeight;
			}
			globalRect.setBottomRight(QPoint(nRight, nBottom));
			break;
		}
		default:
			break;
		}
		m_normalShowRect = globalRect;
		adjustRect(globalRect);
	}

	void WindowImplBase::recoverSizeWhenMaximizedMove(const QPoint& mousePos)
	{
		if (m_eShowState == SHOW_MAXIMIZED)
		{
			QPoint topLeft = this->mapToGlobal(this->rect().topLeft());
			QPoint bottomRight = this->mapToGlobal(this->rect().bottomRight());
			QRect globalRect(topLeft, bottomRight);

			QRect golbalRect = m_normalShowRect;
			int nLeftDistance = mousePos.x() - globalRect.left();
			int nRightDistance = globalRect.right() - mousePos.x();

			// 移动顶部
			golbalRect.moveTop(globalRect.top());

			if (nLeftDistance <= globalRect.width() / 2)
			{
				// 鼠标位置在二分之一宽左边
				if (nLeftDistance <= golbalRect.width() / 2)
				{
					golbalRect.moveLeft(globalRect.left());
				}
				else
				{
					golbalRect.moveLeft(mousePos.x() - golbalRect.width() / 2);
				}
			}
			else
			{
				// 鼠标位置在二分之一宽右边
				if (nRightDistance <= golbalRect.width() / 2)
				{
					golbalRect.moveRight(globalRect.right());
				}
				else
				{
					golbalRect.moveLeft(mousePos.x() - golbalRect.width() / 2);
				}
			}

			m_normalShowRect = golbalRect;
			showNormal();
		}
	}

	void WindowImplBase::stopOperate()
	{
		m_eOpType = OP_NONE;
		m_eDirection = NONE;
		this->releaseMouse();
		this->setCursor(Qt::ArrowCursor);
	}

}//namespace GeneralUI
